package com.lancoo.cvas.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lancoo.cvas.entity.*;
import com.lancoo.cvas.exception.BizException;
import com.lancoo.cvas.mapper.*;
import com.lancoo.cvas.pojo.common.Deleted;
import com.lancoo.cvas.pojo.constant.GlobalConstant;
import com.lancoo.cvas.pojo.response.basicPlatform.BaseGrade;
import com.lancoo.cvas.pojo.vo.GradeDto;
import com.lancoo.cvas.pojo.vo.MajorDto;
import com.lancoo.cvas.pojo.vo.TaskInput;
import com.lancoo.cvas.pojo.vo.WeekDateVo;
import com.lancoo.cvas.service.*;
import com.lancoo.cvas.util.RedisUtil;
import com.lancoo.cvas.util.TimeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service("taskInfoService")
public class TaskInfoServiceImpl extends ServiceImpl<TaskInfoMapper, TaskInfo> implements TaskInfoService {


    @Resource
    private MajorMapper majorMapper;


    @Lazy
    @Resource
    private YunService yunService;

    @Resource
    private TaskProcessService taskProcessService;

    @Resource
    private TaskToGradeService taskToGradeService;

    @Resource
    private CssTaskInfoService cssTaskInfoService;

    @Resource
    private BasicRuleService basicRuleService;

    @Resource(name = "asyncExecutor")
    private ThreadPoolTaskExecutor executor;

    @Resource
    private CourseService courseService;

    @Resource
    private CourseWeekService courseWeekService;

    @Resource
    private CourseSettingService courseSettingService;
    @Resource
    private ClassInfoService classInfoService;
    @Resource
    private UserSettingService userSettingService;
    @Resource
    private PlanTeachingService planTeachingService;
    @Resource
    private PlanTeachingCourseSegmentService planTeachingCourseSegmentService;
    @Resource
    private PlanTeachingStudentService planTeachingStudentService;
    @Resource
    private SegmentService segmentService;
    @Resource
    private StudentMapper studentMapper;

    @Resource
    private ScheduleService scheduleService;

    @Resource
    private TeacherMapper teacherMapper;

    @Resource
    private RoomMapper roomMapper;

    @Resource
    private PeriodSchemeMapper periodSchemeMapper;

    @Resource
    private RuleWeekDayMapper ruleWeekDayMapper;

    @Resource
    private RuleUsableMapper ruleUsableMapper;

    @Resource
    private TeachingClassService teachingClassService;


    @Resource
    private ScheduleConflictInfoService scheduleConflictInfoService;


    @Resource
    private RedisUtil redisUtil;
    @Resource
    private TaskInfoMapper taskInfoMapper;
    @Resource
    private ArrangeProcessService arrangeProcessService;

    @Override
    public List<MajorDto> getMajorByTaskId(Integer taskId, Integer module) {

        //获取专业
        List<MajorDto> majorDtoList = new ArrayList<>();

        majorDtoList = majorMapper.getMajor(taskId);

        return majorDtoList.stream().distinct().collect(Collectors.toList());
    }

    @Override
    public List<GradeDto> getGradeByTaskId(Integer taskId, Integer module) {
        //获取年级
        List<GradeDto> gradeDtoList = new ArrayList<>();
        gradeDtoList = majorMapper.getGrade(taskId);
        return gradeDtoList.stream().sorted(Comparator.comparing(GradeDto::getGradeName)).collect(Collectors.toList());
    }

    @Override
    public List<GradeDto> getGrade(String schoolId, String term) {
        List<GradeDto> gradeList = new ArrayList<>();
        //基础平台获取年级信息 开启计划只有本学期，下学期.暂时不考虑同步年级信息
        List<BaseGrade> baseGrades = yunService.getGrade(schoolId);
        baseGrades.sort(Comparator.comparing(BaseGrade::getOrderNo));
        baseGrades.forEach(baseGrade -> {
            GradeDto gradeDto = new GradeDto();
            gradeDto.setGradeId(baseGrade.getGradeID());
            gradeDto.setGradeName(baseGrade.getGradeName());
            gradeList.add(gradeDto);
        });

        //将已选的年级标记一下
        List<TaskInfo> taskInfos = this.lambdaQuery().eq(TaskInfo::getSchoolId, schoolId)
                .eq(TaskInfo::getTerm, term).eq(TaskInfo::getDeleted, Deleted.NOT.getCode()).list();
        if (CollUtil.isEmpty(taskInfos)) {
            return gradeList;
        }

        taskToGradeService.lambdaQuery().in(TaskToGrade::getTaskId, taskInfos.stream().map(TaskInfo::getTaskId).collect(Collectors.toList())).list().forEach(taskToGrade -> {
            gradeList.forEach(baseGrade -> {
                if (baseGrade.getGradeId().equals(taskToGrade.getGradeId())) {
                    baseGrade.setSelected(true);
                }
            });
        });
        return gradeList;
    }

    @Override
    public synchronized TaskInfo addTask(TaskInput taskInput) {

        //获取年级
        List<String> gradeDtos = taskInfoMapper.getGrade(taskInput.getSchoolId(), taskInput.getTerm());

        if (CollUtil.isNotEmpty(gradeDtos)) {

            List<TaskToGrade> collect = taskInput.getTaskToGrades().stream().filter(p -> gradeDtos.contains(p.getGradeId())).collect(Collectors.toList());

            if (CollUtil.isNotEmpty(collect)) {
                throw new BizException("学期计划年级已存在");
            }
        }

        //获取计划名称
        List<String> taskInfoName = taskInfoMapper.getTaskNameList();

        if (taskInfoName.contains(taskInput.getTaskName())) {
            throw new BizException("学期计划名称已存在");
        }


        TaskInfo taskInfo = new TaskInfo();
        //类型转换
        BeanUtil.copyProperties(taskInput, taskInfo);

        this.saveOrUpdate(taskInfo);
        Integer taskId = taskInfo.getTaskId();
        if (taskId > 0) {
            try {
                taskInput.getTaskToGrades().forEach(taskToGrade -> {
                    taskToGrade.setTaskId(taskId);
                });
                taskToGradeService.saveBatch(taskInput.getTaskToGrades());
                //init 插入流程数据
                taskProcessService.initProcess(taskInfo.getTaskId(), taskInfo.getMainFlow());
                //插入选课计划
                cssTaskInfoService.addCssTask(taskInfo);
                // 初始化基础规则
                basicRuleService.initRuleUsable(taskInfo.getTaskId(), taskInfo.getSchoolId(), taskInfo.getTerm());
                // 初始化排课信息
                arrangeProcessService.saveOrUpdate(new ArrangeProcess().setTaskId(Long.valueOf(taskId))
                        .setUnsuitBase(0).setAllTeachingClass(0).setNormal(0));
            } catch (Exception e) {
                //发生异常需要删除已同步的数据，因为多线程事务会失效。
                removeTaskByIds(Collections.singletonList(taskId));
                log.error("添加任务-{}-失败:{}", taskInfo, e.getMessage());
                throw new RuntimeException("添加任务失败:" + e.getMessage());
            }
        }
        return taskInfo;

    }

    @Override
    public void removeTaskByIds(List<Integer> taskIds) {


        List<TaskInfo> taskInfoList = taskInfoMapper.selectList(new LambdaQueryWrapper<TaskInfo>());

        List<TaskInfo> taskInfos = this.lambdaQuery().in(TaskInfo::getTaskId, taskIds).list();

        this.lambdaUpdate().in(TaskInfo::getTaskId, taskIds).remove();

        taskToGradeService.lambdaUpdate().in(TaskToGrade::getTaskId, taskIds).remove();

        executor.submit(() -> {
            try {
                //延时删除。防止同步数据时未同步完成就执行该方法
                Thread.sleep(5000);

                taskProcessService.lambdaUpdate().in(TaskProcess::getTaskId, taskIds).remove();
                //其他表数据

                //删除对应课程
                courseService.lambdaUpdate().in(Course::getTaskId, taskIds).remove();


                //删除对应行政班
                classInfoService.lambdaUpdate().in(ClassInfo::getTaskId, taskIds).remove();

                //删除对应选课信息
                cssTaskInfoService.removeCssInfo(taskIds);

                //删除对应专业
                majorMapper.delete(new LambdaQueryWrapper<Major>().in(Major::getTaskId, taskIds));

                //删除对应培养方案
                planTeachingService.lambdaUpdate().in(PlanTeaching::getTaskId, taskIds).remove();

                //删除培养方案对应课程
                planTeachingCourseSegmentService.lambdaUpdate().in(PlanTeachingCourseSegment::getTaskId, taskIds).remove();


                //删除培养方案对应学生
                planTeachingStudentService.lambdaUpdate().in(PlanTeachingStudent::getTaskId, taskIds).remove();

                //删除对应环节
                segmentService.lambdaUpdate().in(Segment::getTaskId, taskIds).remove();

                //删除对应学生
                studentMapper.delete(new LambdaQueryWrapper<Student>().in(Student::getTaskId, taskIds));


                taskInfos.forEach(taskInfo -> {
                    //删除学生选课数据
                    redisUtil.del(GlobalConstant.REDIS_PREFIX + taskInfo.getTaskId() + StrUtil.C_COLON + "TeachingClassStudent");
                    if (this.lambdaQuery().eq(TaskInfo::getSchoolId, taskInfo.getSchoolId()).eq(TaskInfo::getTerm, taskInfo.getTerm()).eq(TaskInfo::getDeleted, Deleted.NOT.getCode()).count() == 0) {
                        //多个任务共享资源 本学期没有任务时删除
                        teacherMapper.delete(new LambdaQueryWrapper<Teacher>().eq(Teacher::getSchoolId, taskInfo.getSchoolId()).eq(Teacher::getTerm, taskInfo.getTerm()));
                        roomMapper.delete(new LambdaQueryWrapper<Room>().eq(Room::getSchoolId, taskInfo.getSchoolId()).eq(Room::getTerm, taskInfo.getTerm()));
                        periodSchemeMapper.delete(new LambdaQueryWrapper<PeriodScheme>().eq(PeriodScheme::getSchoolId, taskInfo.getSchoolId()).eq(PeriodScheme::getTerm, taskInfo.getTerm()));
                        ruleWeekDayMapper.delete(new LambdaQueryWrapper<RuleWeekDay>().eq(RuleWeekDay::getSchoolId, taskInfo.getSchoolId()).eq(RuleWeekDay::getTerm, taskInfo.getTerm()));
                    }
                });

                //当相同学校学期尚未删除时，更新taskId
                Map<String, List<TaskInfo>> collect = taskInfoList.stream().collect(Collectors.groupingBy(TaskInfo::getSchoolId));
                collect.forEach((key, value) -> {
                    List<TaskInfo> collect1 = taskInfos.stream().filter(p -> p.getSchoolId().equals(key)).collect(Collectors.toList());
                    Map<String, List<TaskInfo>> collect2 = value.stream().collect(Collectors.groupingBy(TaskInfo::getTerm));
                    collect2.forEach((k, v) -> {
                        List<TaskInfo> collect3 = collect1.stream().filter(p -> p.getTerm().equals(k)).collect(Collectors.toList());
                        if (v.size() > collect3.size()) {
                            v.removeAll(collect3);
                            List<TaskInfo> collect4 = v.stream().sorted(Comparator.comparing(TaskInfo::getTaskId)).collect(Collectors.toList());
                            if (CollUtil.isNotEmpty(collect4)) {
                                TaskInfo taskInfo = collect4.get(0);
                                periodSchemeMapper.update(null, new LambdaUpdateWrapper<PeriodScheme>()
                                        .eq(PeriodScheme::getSchoolId, taskInfo.getSchoolId()).eq(PeriodScheme::getTerm, taskInfo.getTerm())
                                        .set(PeriodScheme::getTaskId, taskInfo.getTaskId()));

                                ruleWeekDayMapper.update(null, new LambdaUpdateWrapper<RuleWeekDay>()
                                        .eq(RuleWeekDay::getSchoolId, taskInfo.getSchoolId()).eq(RuleWeekDay::getTerm, taskInfo.getTerm())
                                        .set(RuleWeekDay::getTaskId, taskInfo.getTaskId()));
                            }
                        }
                    });
                });

                //删除规则表
                ruleUsableMapper.delete(new LambdaQueryWrapper<RuleUsable>().in(RuleUsable::getTaskId, taskIds));
                //删除教学班
                teachingClassService.deleteClassRelated(taskIds);
                //删除基础规则
                basicRuleService.removeAllRule(taskIds);
                //删除课表
                scheduleService.lambdaUpdate().in(Schedule::getTaskId, taskIds).remove();
                //删除课表冲突
                scheduleConflictInfoService.lambdaUpdate().in(ScheduleConflictInfo::getTaskId, taskIds).remove();
                //删除用户信息
                userSettingService.lambdaUpdate().in(UserSetting::getTaskId, taskIds).remove();

            } catch (Exception ex) {

                ex.printStackTrace();
            }
        });


    }

    @Override
    @Cacheable(cacheNames = "countWeek", key = "#taskId")
    public WeekDateVo countWeek(Integer taskId) {
        WeekDateVo weekDateVo = new WeekDateVo();
        List<WeekDateVo.WeekDate> weekDates = new ArrayList<>();

        TaskInfo taskInfo = this.getById(taskId);
        LocalDateTime termBeginTime = taskInfo.getTermBeginTime();
        LocalDateTime termEndTime = taskInfo.getTermEndTime();
        long totalWeek = TimeUtils.getTotalWeek(termBeginTime, termEndTime);
        weekDateVo.setWeekCount(totalWeek);
        for (int i = 1; i <= totalWeek; i++) {
            LocalDateTime startWeek;
            LocalDateTime endWeek;
            if (i == 1) {
                //第一周
                startWeek = termBeginTime;
                endWeek = startWeek.with(DayOfWeek.SUNDAY);
            } else if (i == totalWeek) {
                //第二周
                endWeek = termEndTime;
                startWeek = endWeek.with(DayOfWeek.MONDAY);
            } else {
                //中间周
                startWeek = termBeginTime.with(DayOfWeek.MONDAY).plusWeeks(i - 1);
                endWeek = startWeek.with(DayOfWeek.SUNDAY);
            }
            weekDates.add(new WeekDateVo.WeekDate().setWeek(i).setStart(startWeek).setEnd(endWeek));
        }
        weekDateVo.setWeekDates(weekDates);
        return weekDateVo;
    }


}